﻿
using System.Globalization;
using System.Net;
using ComplexNetCentralityLib;
using GraphMLComplexType;

namespace CARE.Web.Services
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Ria;
    using System.Web.DomainServices;
    using System.IO;
    using System.Reflection;
    using EpidemicSimulator;
    using GraphInformation;
    using QuickGraph;
    using QuickGraph.Serialization;
    using ComplexNetGeneratorLib;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel;
    using EpidemicSimulator.states;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.Linq;

    [XmlRoot(ElementName = "Method")]
    public class MethodInvocation
    {
        [XmlAttribute("Name")]
        public String Name { get; set; }

        [XmlAttribute("Condition")]
        public Int32 Condition { get; set; }

        [XmlArray(ElementName = "Params")]
        [XmlArrayItem(Type = typeof(MethodInvocationParam), ElementName = "param")]
        public MethodInvocationParam[] param { get; set; }
    }

    public class MethodInvocationParam
    {
        public String name { get; set; }

        public String type { get; set; }
    }

    // TODO: Create methods containing your application logic.
    [EnableClientAccess()]
    public class GraphProvider : DomainService
    {
        [Query(HasSideEffects = true)]
        public List<Graph> GetDiseaseModelGraph(string modelML)
        {            
            Graph resultG = new Graph();
            resultG.ID = Guid.NewGuid();
            var g = new AdjacencyGraph<DiseaseModelNode, DiseaseModelEdge>();
            StringReader reader = new StringReader(modelML);
            try
            {
                g.DeserializeFromGraphML(
                    reader,
                    id => new DiseaseModelNode(),
                    new IdentifiableEdgeFactory<DiseaseModelNode, DiseaseModelEdge>(SimulationFactory.CreateEdge<DiseaseModelNode, DiseaseModelEdge>)
                );
            }
            catch (Exception e)
            {
                throw e;
            }
            reader.Close();

            var map = new Dictionary<DiseaseModelNode, GraphElement>();
            resultG.Vertices = new List<GraphElement>();
            //var list = new List<GraphElement>();
            for (int i = 0; i < g.VertexCount; i++)
            {
                var v = g.Vertices.ElementAt(i);
                var elem = new GraphElement();
                elem.ID = Guid.NewGuid();
                elem.GraphID = resultG.ID;
                elem.Keys = new List<PairKeyValue>();
                elem.Keys.Add(new PairKeyValue() {Key = "nodeColor", Value = v.nodeColor, ID = Guid.NewGuid(), ElementID = elem.ID});
                elem.Keys.Add(new PairKeyValue() { Key = "nodeX", Value = v.nodeX, ID = Guid.NewGuid(), ElementID = elem.ID });
                elem.Keys.Add(new PairKeyValue() { Key = "nodeY", Value = v.nodeY, ID = Guid.NewGuid(), ElementID = elem.ID });
                elem.Keys.Add(new PairKeyValue() { Key = "stateName", Value = v.stateName, ID = Guid.NewGuid(), ElementID = elem.ID });
                resultG.Vertices.Add(elem);
                map.Add(v, elem);
            }

            var list1 = new List<GraphEdge>();
            resultG.Edges = new List<GraphEdge>();
            for (int i = 0; i < g.Edges.Count(); i++)
            {
                var e = g.Edges.ElementAt(i);
                resultG.Edges.Add(new GraphEdge(){GraphID = resultG.ID, ID = Guid.NewGuid(), Source = map[e.Source].ID, Target = map[e.Target].ID, Condition = e.condition});
            }

            List<Graph> a = new List<Graph>();
            a.Add(resultG);
            return a;
        }

        [Query(HasSideEffects = true)]
        public List<Graph> StartSimulation(XElement simulationSetupSL, String token)
        {
            //TODO: convert xelement to SimulationSetupSL
            SimulationSetupSL simsetup = new SimulationSetupSL();

            Graph resultG = new Graph();

            try
            {
                Simulation s = null;

                if (simsetup.networkGraph != null)
                {
                    var graph = simsetup.networkGraph.GetGraphFormSilverlight();
                }

                SimulationSetup simulationSetup = new SimulationSetup(simsetup.networkGraphML, simsetup.diseaseModelGraphML, simsetup.maxContactsPerStep, simsetup.minContactsPerStep);
                simulationSetup.condition = simsetup.condition;
                simulationSetup.networkGraph = simsetup.networkGraph;
                simulationSetup.stateChangePlansSL = simsetup.stateChangePlansSL;
                simulationSetup.stopConditionsSL = simsetup.stopConditionsSL;
                s = SimulationFactory.createSimulation(simulationSetup, HttpContext.Current.Server.MapPath("App_Data"));

                resultG = s.getSimulationResultForSilverlight();

                HttpContext.Current.Session["simulation"] = s;
            }
            catch (Exception e)
            {
                //TODO: add handling
                Console.Write(e.StackTrace.ToString());
            }
            List<Graph> a = new List<Graph>();
            a.Add(resultG);
            return a;
        }


        [Query(HasSideEffects = true)]
        public List<Graph> GetGraphFromGraphML(String graphML)
        {
            Simulation s = null;
            SimulationSetup simsetup = new SimulationSetup(graphML, null, 1, 1);
            s = SimulationFactory.createSimulation(simsetup, HttpContext.Current.Server.MapPath(@"~/App_Data"));
            var resultGraph = s.getSimulationResultForSilverlight();
            List<Graph> result = new List<Graph>();
            result.Add(resultGraph);
            return result;
        }

        [ServiceOperation]
        public List<String> GetDiseaseModelStateNames()
        {
            List<String> states = new List<string>();

            Type t = typeof(State);

            Assembly asm = t.Assembly;

            foreach (Type type in asm.GetTypes())
            {
                if (type.BaseType == t)
                {
                    String val = type.FullName;
                    states.Add(val);
                }
            }

            return states;
        }

        [ServiceOperation]
        public string[] GetAvailableMethodsOfGenerator()
        {
            MethodInfo[] methods = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethods();
            List<String> toReturn = new List<string>();

            foreach (var item in methods)
                if (item.Name.StartsWith("generate"))
                    toReturn.Add(item.Name);

            return toReturn.ToArray();
        }

        [ServiceOperation]
        public string[] GetParametersFromMethod(string methodName)
        {
            MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
            List<string> toReturn = new List<string>();

            ParameterInfo[] parameters = method.GetParameters();
            foreach (var p in parameters)
                toReturn.Add(p.Name);

            return toReturn.ToArray();
        }

        [ServiceOperation]
        public string[] GetParametersTypesFromMethod(string methodName)
        {
            MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
            ParameterInfo[] parameters = method.GetParameters();
            string[] types = new string[parameters.Length];

            int i = 0;
            foreach (var item in parameters)
                types[i++] = item.ParameterType.ToString();

            return types;
        }

        private Graph GenerateGraph(string methodName, string[] param, string[] paramTypes, int condition)
        {
            MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
            ComplexNetGenerator generator = new ComplexNetGenerator();

            int j = 0;
            object[] paramObj = new object[param.Count()];
            for(int i = 0; i < param.Count(); i++)
            {
                switch (paramTypes[j++])
                    {
                        case "System.Int32":
                            paramObj[i] = int.Parse(param[i]);
                            break;
                        case "System.Double":
                            paramObj[i] = double.Parse(param[i]);
                            break;
                        default:
                            break;
                    }         
            }

            ComplexNet net = method.Invoke(generator, paramObj) as ComplexNet;

            Graph graph = CreateGraphFromComplexNet(net);

            // TODO: dane osob z BD
            //DataBaseClassesDataContext db = new DataBaseClassesDataContext();
            //var nodes = from n in db.Nodes
            //            select n;
            //var nodesArray = nodes.ToArray();

            //Random random = new Random();
            //foreach (var vertex in graph.Vertices)
            //{
            //    var n = nodesArray[random.Next() % nodesArray.Length];

            //    vertex.Keys.Add(new PairKeyValue("birthdate", n.birthdate.ToString()));
            //    vertex.Keys.Add(new PairKeyValue("country", n.country));
            //    vertex.Keys.Add(new PairKeyValue("image", n.image.ToString()));//zmiana spowodowana innym sposobem przechowywania zdjec
            //    vertex.Keys.Add(new PairKeyValue("latitude", n.latitude.ToString()));
            //    vertex.Keys.Add(new PairKeyValue("longitude", n.longitude.ToString()));
            //    vertex.Keys.Add(new PairKeyValue("name", n.name));
            //    vertex.Keys.Add(new PairKeyValue("postalcode", n.postalcode));
            //    vertex.Keys.Add(new PairKeyValue("street", n.street));
            //    vertex.Keys.Add(new PairKeyValue("surname", n.surname));
            //    vertex.Keys.Add(new PairKeyValue("town", n.town));
            //}

            foreach (var edge in graph.Edges)
            {
                edge.Condition = condition;
			}


            return graph;
        }

        [Query(HasSideEffects = true)]
        public List<Graph> GetGeneratedGraph(XElement param)
        {        
            XmlSerializer ser = new XmlSerializer(typeof(MethodInvocation));
            var invoc = (MethodInvocation)ser.Deserialize(param.CreateReader());
            var paramNames = from p in invoc.param select p.name;
            var paramTypes = from p in invoc.param select p.type;

            List<Graph> a = new List<Graph>();            
            a.Add(GenerateGraph(invoc.Name, paramNames.ToArray(), paramTypes.ToArray(), invoc.Condition));
            return a;
        }

        [ServiceOperation]
        public String GenerateGraphAndSaveGraphML(string methodName, string[] param, string[] paramTypes, int condition)
        {
            return CreateGraphMLFromGraph(GenerateGraph(methodName, param, paramTypes, condition));
        }

        private Graph CreateGraphFromComplexNet(ComplexNet net)
        {
            Graph g = new Graph();
            g.ID = Guid.NewGuid();
            Dictionary<ComplexNetNode, GraphElement> map = new Dictionary<ComplexNetNode, GraphElement>();
            int i = 0;
            g.Vertices = new List<GraphElement>();
            foreach (ComplexNetNode node in net.nodes)
            {
                GraphElement v = new GraphElement()
                {
                    ID = Guid.NewGuid(),
                    GraphID = g.ID,
                    Keys = new List<PairKeyValue>()
                };
                v.Keys.Add(new PairKeyValue()
                {
                    ID = Guid.NewGuid(),
                    Key = v.ID + "_Key",
                    Value = v.ID + "_Value"
                });
                map.Add(node, v);
                g.Vertices.Add(v);
            }

            i = 0;
            g.Edges = new List<GraphEdge>();
            foreach (ComplexNetEdge edge in net.edges)
            {
                GraphEdge e = new GraphEdge()
                {
                    ID = Guid.NewGuid(),
                    GraphID = g.ID,
                    Source = map[edge.source].ID,
                    Target = map[edge.target].ID
                };
                g.Edges.Add(e);
            }

            return g;
        }

        private string CreateGraphMLFromGraph(Graph g)
        {
            //int condition = 0;

            String siec = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Environment.NewLine +
            "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns " +
            "http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">" + Environment.NewLine +
            "  <key id=\"id\" for=\"node\" attr.name=\"id\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"name\" for=\"node\" attr.name=\"name\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"surname\" for=\"node\" attr.name=\"surname\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"birthdate\" for=\"node\" attr.name=\"birthdate\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"photo\" for=\"node\" attr.name=\"photo\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"location\" for=\"node\" attr.name=\"location\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"locationX\" for=\"node\" attr.name=\"locationX\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"locationY\" for=\"node\" attr.name=\"locationY\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"street\" for=\"node\" attr.name=\"street\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"town\" for=\"node\" attr.name=\"town\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"postalcode\" for=\"node\" attr.name=\"postalcode\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"country\" for=\"node\" attr.name=\"country\" attr.type=\"string\" />" + Environment.NewLine +
            "  <key id=\"stateName\" for=\"node\" attr.name=\"stateName\" attr.type=\"string\" />" + Environment.NewLine +
            " <graph edgedefault=\"undirected\">" + Environment.NewLine;

            int j = 1;
            String xmlNode;
            foreach (var node in g.Vertices)
            {
                xmlNode = "<node id=\"" + Convert.ToString(node.ID) + "\">" + Environment.NewLine;

                xmlNode +=
        "      <data key=\"id\">" + Convert.ToString(node.ID) + "</data>" + Environment.NewLine +
        "      <data key=\"name\">name" + j + "</data>" + Environment.NewLine +
        "      <data key=\"surname\">surname" + j + "</data>" + Environment.NewLine +
        "      <data key=\"birthdate\">" + DateTime.Now + "</data>" + Environment.NewLine +
        "      <data key=\"photo\">http://nosoifighters.studentlive.pl/GetImage.aspx?id=44</data>" + Environment.NewLine +
        "      <data key=\"location\">location" + j + "</data>" + Environment.NewLine +
        "      <data key=\"locationX\">0</data>" + Environment.NewLine +
        "      <data key=\"locationY\">0</data>" + Environment.NewLine +
        "      <data key=\"street\">street" + j + "</data>" + Environment.NewLine +
        "      <data key=\"town\">town" + j + "</data>" + Environment.NewLine +
        "      <data key=\"postalcode\"></data>" + Environment.NewLine +
        "      <data key=\"country\">country" + j + "</data>" + Environment.NewLine +
        "      <data key=\"stateName\">Susceptible</data>" + Environment.NewLine + "</node>" + Environment.NewLine;


                //TODO: jesli zostanie skonczony edytor
                //String key = "<data key=\"";
                //HashSet<String>.Enumerator e = node.attributes.GetEnumerator();
                //while (e.MoveNext())
                //{
                //    String attName = e.ToString();
                //    String att = e.Current;
                //}
                siec += xmlNode;

                j++;
            }

            String xmlEdge;
            int i = 0;
            foreach (var edge in g.Edges)
            {
                xmlEdge = "<edge id=\"" + (i++) + "\" source=\"" + edge.Source + "\" target=\"" + edge.Target + "\" condition=\"" + edge.Condition + "\" " + "/>" + Environment.NewLine;
                siec += xmlEdge;
            }

            siec += "  </graph>" + Environment.NewLine + "</graphml>";

            return siec;
        }
       
        [ServiceOperation]
        public string[] GetAvailableStateChangeStrategies()
        {
            List<String> strategies = new List<string>();
            Type t = typeof(EpidemicSimulator.strategies.StateChangeStrategy);
            Assembly asm = t.Assembly;
            foreach (Type type in asm.GetTypes())
            {
                Type[] interfaces = type.GetInterfaces();
                foreach (Type interFace in interfaces)
                {

                    if (interFace.FullName == t.FullName)
                    {
                        String val = type.Name;
                        strategies.Add(val);
                    }
                }
            }
            return strategies.ToArray();
        }

        [ServiceOperation]
        public String[] GetAvailableMethodsOfCentrality()
        {
            MethodInfo[] methods = typeof(ComplexNetCentrality).GetMethods(BindingFlags.Static | BindingFlags.Public);
            List<String> toReturn = new List<string>();

            foreach (var item in methods)
            {
                ParameterInfo[] infos = item.GetParameters();
                if (infos.Count() == 1)
                {
                    if (infos[0].Name == "socialNetwork")
                        toReturn.Add(item.Name);
                }
            }

            return toReturn.ToArray();
        }

        [Query(HasSideEffects = true)]
        public List<Graph> GetComputedCentralityForGraph(String MethodName, XElement inputGraph)
        {
            XmlSerializer ser = new XmlSerializer(typeof(Graph));
            Graph input = (Graph) ser.Deserialize(inputGraph.CreateReader());

            UndirectedGraph<SocialNetworkNode, EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode>> graph = input.GetGraphFormSilverlight();
            AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>> network = new AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>>();
            foreach (EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode> edge in graph.Edges)
            {
                EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType> e = new EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>(edge.Source, edge.Target);
                network.AddVerticesAndEdge(e);
            }

            Type complex = typeof(ComplexNetCentralityLib.ComplexNetCentrality);
            Graph centralityGraph = null;
            try
            {
                centralityGraph = (Graph)complex.GetMethod(MethodName).Invoke(null, new object[] { network });
                input.Merge(network);
            }
            catch (Exception e)
            {
                ;
            }

            List<Graph> a = new List<Graph>();
            a.Add(input);
            return a;
        }

        [ServiceOperation]
        public double[] GetGeoCode(string strAddress)
        {
            string strKey = "ABQIAAAAt21w088otvkx4dydGkSFzhTiWYdC4kK5HJo75hRqCbZEyPurWxRFE2iDTU5WiKeD_zuNhg5zVaDZbw";

            string sPath = "http://maps.google.com/maps/geo?q=" + strAddress + "&output=csv&key=" + strKey;


            WebClient client = new WebClient();

            string[] eResult = client.DownloadString(sPath).ToString().Split(',');

            string latitude = eResult.GetValue(2).ToString();
            string longitude = eResult.GetValue(3).ToString();

            return new double[] { Convert.ToDouble(latitude, NumberFormatInfo.InvariantInfo), Convert.ToDouble(longitude, NumberFormatInfo.InvariantInfo) };
        }
    }
}


